#!/usr/bin/env node
/** @format */

import fs from 'fs'
import path from 'path'
import { startRep } from './collect.js'
import { strToObj, isFileExisted, loadChildModule } from './utils.js'

let globalConfig = {}

let curModuleName = ''

// 每个模块的cn字典
let currentModuleDictMap = null

// 正则转译
function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}

// 递归当前目录所有文件
function walkSync(currentDirPath, callback) {
  fs.readdirSync(currentDirPath, { withFileTypes: true })?.forEach((dirent) => {
    const fullPath = path.join(currentDirPath, dirent.name)
    if (dirent.isFile()) {
      callback(fullPath)
    } else if (dirent.isDirectory()) {
      walkSync(fullPath, callback)
    }
  })
}

function matchingFile() {
  const langSourcePath = []
  const replacePath = []
  const modulesName = []
  Object.keys(globalConfig.entry)?.forEach((item) => {
    const obj = globalConfig.entry[item]
    langSourcePath.push(path.resolve(obj.i18nSource))
    replacePath.push(path.resolve(obj.path))
    modulesName.push(item)
  })
  return {
    langSourcePath, //字典
    replacePath, //文件
    modulesName,
  }
}

const splitFile = (str, file) => {
  if (file.indexOf('.js') !== -1) {
    return scriptHandle(str, file)
  }
  // vue
  const temp = /<template>[\s\S]*<\/template>/gi
  const jsReg = /<script\s*(setup)*\s*\>[\s\S]*<\/script>/gi
  // 处理模版
  str = str.replace(temp, function (word) {
    return templateHandle(word, file)
  })
  // 处理js
  str = str.replace(jsReg, function (word) {
    return scriptHandle(word, file)
  })
  return str
}

const findCode = (word, file, quotation) => {
  if (!word) return ''
  let code = ''
  const arrs = Object.keys(currentModuleDictMap)
  for (let i = 0; i < arrs.length; i++) {
    if (quotation) {
      // `
      const arr = []
      let placeholderIdx = -1
      // 转成新的文本，和i18n中的code保持一致
      const newword = word.replace(/\${([\s\S]*?)}/g, function (code, $1) {
        $1 && arr.push($1)
        ++placeholderIdx
        return `{placeholder_${placeholderIdx}}`
      })
      let objStr = ''
      arr.forEach((item, idx) => {
        objStr =
          objStr +
          `placeholder_${idx}` +
          ': ' +
          item +
          (idx < arr.length - 1 ? ', ' : '')
      })
      objStr = `{${objStr}}`

      if (currentModuleDictMap[arrs[i]] === newword) {
        const i18nWord = `${arrs[i]}`
        // console.log(curModuleName ,globalConfig.entry, i18nWord)
        if (arr.length > 0) {
          code = `${globalConfig.i18nT}('${i18nWord}', ${objStr})`
        } else {
          code = `${globalConfig.i18nT}('${i18nWord}')`
        }
        break
      }
    } else {
      // ' or "
      if (currentModuleDictMap[arrs[i]] === word) {
        const i18nWord = `${arrs[i]}`
        code = `${globalConfig.i18nT}('${i18nWord}')`
        break
      }
    }
  }
  if (word?.trim() && !code) {
    console.error(`${file},未匹配到${word}`)
  }
  return code
}

// 处理vue中template
const templateHandle = (word, file) => {
  const curData = globalConfig.saveReplaceObj[file]
  if(!curData) {
    console.log('file', file)
  }
  curData?.attrData?.forEach((item) => {
    item.attrsDataObj?.forEach((cur) => {
      word = word.replace(
        new RegExp(escapeRegExp(cur.attr), 'g'),
        function (curword) {
          const code = findCode(cur.value, file)
          if (code) {
            return `:${cur.name}="${code}"`
          } else {
            return curword
          }
        }
      )
    })
  })
  const arr = []
  const arr2 = []
  // 保护textData 不会影响到attr
  // 防止 => 导致标签不完整匹配
  word = word.replace(/<([\w\-]+)([\s\S]*?)(=>)/g, function (curword, $1, $2) {
    return '<' + $1 + $2 + 'arrowFn'
  })
  // 过滤出标签属性 防止替换了属性
  word = word.replace(/<([\w\-]+)([\s\S]*?)>/g, function (curword, $1, $2) {
    arr.push(curword)
    return `<custom_Tag>`
  })
  // 过滤出已经是动态变量的{{ Change }} =>  {{ {{Change}} }} 不该翻译了的翻译了
  word = word.replace(/{{[\s\S]*?}}/g, function (curword, $1) {
    arr2.push(curword)
    return `{{noData}}`
  })
  curData.textData = curData?.textData?.sort?.((a, b) => b.length - a.length)
  curData.textData?.forEach((item) => {
    // 有瑕疵 如果vin出现在前面 会把 signvin 替换掉，导致sig{{code}} 因此要先长后短
    word = word.replace(
      new RegExp(escapeRegExp(item), 'g'),
      function (curword) {
        const code = findCode(item, file)
        if (code) {
          return `{{ ${code} }}`
        } else {
          return curword
        }
      }
    )
  })
  // 还原attr
  word = word.replace(/<([\w\-]+)([\s\S]*?)>/g, function () {
    return arr.shift()
  })
  //  还原{{}}
  word = word.replace(/{{noData}}/g, function () {
    return arr2.shift()
  })
  // 还原箭头函数
  word = word.replace(
    /<([\w\-]+)([\s\S]*?)(arrowFn)/g,
    function (curword, $1, $2) {
      return '<' + $1 + $2 + '=>'
    }
  )
  return word
}

function findStrSubtring(str, cha) {
  let index = str.indexOf(cha)
  let positions = []
  while (index !== -1) {
    positions.push(index)
    index = str.indexOf(cha, index + 1)
  }
  return positions
}

function preVaild(word, globalConfig, curword, IDX) {
  try {
    let newStr = word.replace(/\n/gi, '').replace(/\s/gi, '')
    let validCharacter = ''
    let i = 1
    let end = 0
    // 得到最大匹配范围
    globalConfig.hotToken.forEach((item) => {
      if (item.length > end) {
        end = item.length
      }
    })

    // 多找5个的容错范围
    end += 5
    const idx = newStr.indexOf(IDX)

    while (validCharacter.length < end) {
      validCharacter = newStr[idx - i] + validCharacter
      i++
      // 最多循环80次
      if (i > 80) {
        break
      }
    }
    let bool = false
    let strFirst = ''
    let strLast = ''
    for (let i = 0; i < globalConfig.hotToken.length; i++) {
      const index = validCharacter.length - globalConfig.hotToken[i].length - 3
      const newvalidCharacter = validCharacter.substring(index)
      let idx2 = newvalidCharacter.indexOf(globalConfig.hotToken[i])
      // 情况1 可能对象的属性obj.message,'xxx'
      // 情况2 label="aaa" jsx
      // 情况3 label == "aaa" ? 'a' : 'b';
      strFirst = newvalidCharacter[newvalidCharacter.length - 1]
      strLast =
        newvalidCharacter[newvalidCharacter.length - 1] +
        newvalidCharacter[newvalidCharacter.length - 2]
      if (idx2 !== -1 && strFirst !== ',' && strLast !== '==') {
        bool = true
        break
      }
    }
    return {
      valid: bool,
      isJsx: strFirst === '=',
    }
  } catch (e) {
    console.error(e)
  }
}

// 处理script脚本
const scriptHandle = (word, file) => {
  const curData = globalConfig.saveReplaceObj[file]
  // console.log(curData);
  const reg = /(`|'|")([\s\S]*?)\1/g
  let curIdx = -1
  let arr = []
  const arrObj = {}
  word = word.replace(reg, function (cur, $0, $1) {
    // 为什么可以不等于` 因为代码${}被我们转译了
    if (!curData?.jsTextData?.includes($1) && $0 !== '`') return cur
    if ($0 === '`') {
      let placeholderIdx = -1
      // 转成新的文本，和i18n中的code保持一致
      const newword = $1.replace(/\${([\s\S]*?)}/g, function (code, $1) {
        ++placeholderIdx
        return `{placeholder_${placeholderIdx}}`
      })
      // console.log('item:', item,'\n', newword,  '\n', cur, item === newword)
      if (!curData?.jsTextData?.includes(newword)) return cur
    }
    arr.push(cur)
    curIdx++
    const name = `needFind_${curIdx}`
    arrObj[name] = {}
    arrObj[name].content = $1
    arrObj[name].allContent = cur
    arrObj[name].quotation = $0
    return name
  })

  arr.forEach((cur, idx) => {
    const IDX = `needFind_${idx}`
    // 进行前置判断
    const { valid, isJsx } = preVaild(word, globalConfig, cur, IDX)
    if (valid) {
      const code = findCode(arrObj[IDX].content, file, arrObj[IDX].quotation)
      // const code = findCode($1, file, $0);
      if (code) {
        if (isJsx) {
          word = word.replace(IDX, `{ ${code}} `)
        }
        word = word.replace(IDX, code)
        // return code;
      } else {
        word = word.replace(IDX, cur)
        // return cur;
      }
    } else {
      word = word.replace(IDX, cur)
      // return cur;
    }
  })

  return word
}

// 处理忽略
const ignoreFn = (word) => {
  const ignore = /(\/\*[\s\S]*?\*\/|<!--[\s\S]*?-->|\/\/.*)/gi
  const stack = []
  // 零时数据
  stack.token = '_zeroData'
  word = word.replace(/(http(s)*:)\/\//gi, function (content, $1) {
    return $1 + '||'
  })
  word = word.replace(ignore, function (content) {
    stack.push(content)
    return stack.token
  })
  return {
    word,
    stack,
  }
}

function revertIgnore(newStr, stack) {
  // 编译正则表达式一次
  const tokenRegex = new RegExp(stack.token, 'gi')
  const httpRegex = /(http(s)*:)\|\|/gi

  // 使用缓存的正则表达式
  newStr = newStr.replace(tokenRegex, () => {
    return stack.shift()
  })

  newStr = newStr.replace(httpRegex, (content, $1) => {
    return $1 + '//'
  })

  return newStr
}

const handleFiles = (files) => {
  files.forEach((file) => {
    try {
      const data = fs.readFileSync(file, 'utf8')
      // 暂存注释代码
      const { word, stack } = ignoreFn(data)
      let newStr = splitFile(word, file)
      newStr = revertIgnore(newStr, stack)
      fs.writeFileSync(file, newStr, { flag: 'w+' })
    } catch (e) {
      console.error('Error:', file)
    }
  })
}

export async function replace(config) {
  try {
    const res = await startRep(config)

    globalConfig = res
    // 字典path路径 和 要翻译模块路径
    const { langSourcePath, replacePath, modulesName } = matchingFile()
    replacePath.forEach((modulePath, idx) => {
      // 获取字典中的词条
      const fileData = fs.readFileSync(langSourcePath[idx], 'utf8')
      currentModuleDictMap = strToObj(fileData)
      // 当前模块
      curModuleName = modulesName[idx]
      // 获取对应模块文件
      const files = loadChildModule(modulePath, globalConfig.entry[curModuleName])
      console.log('处理模块', curModuleName, files.length)
      handleFiles(files)
    })
    console.log('程序替换完成～')
  } catch (e) {
    throw new Error(e)
  }
}
